package org.example.lib;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import lombok.SneakyThrows;
import org.example.annotations.route.RequestMapping;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Route {

   @Getter
   private String path = null;
   @Getter
   private String regularPath = null;
   private final List<String> paramNames = new ArrayList<>();
   @Setter
   private Method method = null;

   public Route(String path) {
      if (path.contains("{")) {
         Pattern compile = Pattern.compile("\\{([^}]+)}");
         Matcher matcher = compile.matcher(path);
         while (matcher.find()) {
            paramNames.add(matcher.group(1));
         }
         regularPath = path.replaceAll("\\{([^}]+)}", "(?<$1>[^/]+)");
      }else{
         this.path = path;
      }
   }

   public boolean match(String path) {
      if (this.regularPath != null) {
         return path.matches(this.regularPath);
      }
      return this.path.equals(path);
   }

   public boolean isStatic() {
      return this.regularPath == null;
   }

   public Map<String, Object> getRouteParam(String path) {
      Map<String, Object> params = new HashMap<>();
      if (isStatic()) {
         return params;
      }

      Pattern compile = Pattern.compile(this.regularPath);
      Matcher matcher = compile.matcher(path);
      if (matcher.matches()) {
         this.paramNames.forEach(paramName -> params.put(paramName, matcher.group(paramName)));
      }

      return params;
   }



   @SneakyThrows
   public Object invoke(Map<String, Object> params, HttpServletRequest request, HttpServletResponse response) {
      Class<?> declaringClass = method.getDeclaringClass();
      IocContainer instance   = IocContainer.instance;
      Object bean = instance.getBean(declaringClass);

      ArrayList<Object> invokeParams = new ArrayList<>();
      for (Parameter parameter : method.getParameters()) {
         String name = parameter.getName();
         if (parameter.getType().isLocalClass()
             || parameter.getType().isPrimitive()
            ||  parameter.getType().equals(String.class)
            ||  parameter.getType().equals(Integer.class)
            ||  parameter.getType().equals(Long.class)
            ||  parameter.getType().equals(Double.class)
            ||  parameter.getType().equals(Float.class)
            ||  parameter.getType().equals(Boolean.class)
            ||  parameter.getType().equals(Character.class)
            ||  parameter.getType().equals(Byte.class)
            ||  parameter.getType().equals(Short.class)
            ||  parameter.getType().equals(List.class)
         ){
            invokeParams.add(params.get(name));
         }else{
            invokeParams.add(instance.getBean(parameter.getType()));
         }
      }

      return method.invoke(bean, invokeParams.toArray());
   }
}
